/**
 * 
 */
package robocup.component.worldmodel.particlefilter;

import java.util.Random;

import robocup.component.geometry.Vektor;
import robocup.component.infotypes.FlagInfo;

/**
 * @author werwurm
 * 
 */
public class Region {

  private Vektor origin;

  private double minDistance;

  private double maxDistance;

  private double minDirection;

  private double directionDelta;

  private final Vektor helperVektor = new Vektor();

  private final Random random       = new Random(System.currentTimeMillis());

  /**
   * A defined region for particles.
   * 
   * @param fi -
   *          a seen flag, for which the region should be used.
   * @param headAngle -
   *          the headAngle, needed to define the region
   * @param flagPosition -
   *          the fixed position of the flag
   */
  public Region(
      final FlagInfo fi,
      final double headAngle,
      final Vektor flagPosition) {

    super();
    this.origin = flagPosition;

    this.minDistance = Math
        .log(((double) Math.rint(fi.getDistance() / 0.1) - 0.5) * 0.1);
    this.minDistance = Math
        .exp(((double) Math.rint(this.minDistance / 0.01) - 0.5) * 0.01);
    this.maxDistance = Math
        .log(((double) Math.rint(fi.getDistance() / 0.1) + 0.5) * 0.1);
    this.maxDistance = Math
        .exp(((double) Math.rint(this.maxDistance / 0.01) + 0.5) * 0.01);

    this.minDirection = 180 + fi.getDirection() + headAngle - 1;
    final double maxDirection = 180 + fi.getDirection() + headAngle + 1;
    this.directionDelta = (new Vektor(
        1,
        (maxDirection < this.minDirection ? maxDirection + 360 : maxDirection) - this.minDirection))
        .getAngle();
  }

  public void reinitRegionFlag(final FlagInfo fi,
      final double headAngle,
      final Vektor flagPosition) {

    this.origin = flagPosition;

    this.minDistance = Math
        .log(((double) Math.rint(fi.getDistance() / 0.1) - 0.5) * 0.1);
    this.minDistance = Math
        .exp(((double) Math.rint(this.minDistance / 0.01) - 0.5) * 0.01);
    this.maxDistance = Math
        .log(((double) Math.rint(fi.getDistance() / 0.1) + 0.5) * 0.1);
    this.maxDistance = Math
        .exp(((double) Math.rint(this.maxDistance / 0.01) + 0.5) * 0.01);

    this.minDirection = 180 + fi.getDirection() + headAngle - 1;
    this.directionDelta = 2.0;
  }

  public void reinitRegionSpeed(final Vektor speed,
      final Vektor origin) {

    this.origin = origin;
    final double speedHelper = speed.getLength();
    this.minDistance = speedHelper - 0.005;
    this.maxDistance = speedHelper + 0.005;

    this.minDirection = speed.getAngle() - .5;
    this.directionDelta = 1.0;
  }

  public Vektor makeRandomInsideRegion(Vektor point) {

    if (point == null) {
      point = new Vektor();
    }

    point
        .setLength(this.minDistance + (this.maxDistance - this.minDistance) * this.random
            .nextDouble());
    point.rotate(-point.getAngle() + this.minDirection + (2 * this.random
        .nextDouble()));
    return point.addToThis(this.origin);
  }

  public void setOrigin(final Vektor origin) {

    this.origin = origin;
  }

  public double insideRegion(final Vektor point) {

    this.helperVektor.x = point.x;
    this.helperVektor.y = point.y;
    this.helperVektor.subFromThis(this.origin);
    this.helperVektor.rotate(-this.minDirection);
    if (this.helperVektor.getAngle() <= this.directionDelta && this.helperVektor
        .getAngle() >= 0 && this.helperVektor.getLength() >= this.minDistance && this.helperVektor
        .getLength() <= this.maxDistance) {
      return 1.0;
    }
    else {
      return -1;
    }
  }
}
